
(function (that) {

	/*
	 * Unique ID counter for adding events
	 */
	var event_guid = 1;



	/*
	 * Cross-browser event handling
	 *
	 * Written by Dean Edwards, 2005
	 * with input from Tino Zijdel, Matthias Miller, Diego Perini
	 * http://dean.edwards.name/weblog/2005/10/add-event/
	 *
	 * @param event 	Default object handed by event emitters
	 * @return 			False if any event-handling method returns false, otherwise true.
	*/
	var handle_event = function (event) {

		var result = true;

		// grab the event object (IE uses a global event object)
		event = fix_event(event || ((this.ownerDocument || this.document || this).parentWindow || window).event);

		// get a reference to the hash table of event handlers
		var handlers = this.events[event.type];

		// execute each event handler
		for (var i in handlers) {
			this.$$handle_event = handlers[i];
			if (this.$$handle_event(event) === false) {
				result = false;
			}
		}

		return result;
	};



	/*
	 * Normalize events for all Browsers
	 * Taken from jQuery
	 * @param event		The event object to extend
	 * @return The same extended event object
	 */
	var fix_event = function( event ) {

		// Normalize bubbling for IE by
	 	// adding standard W3C event methods
		if(!event.preventDefault){
			event.preventDefault		= function () { this.returnValue = false; };
			event.stop_propagation	= function () { this.returnValue = true; };
		}

		// Fix target property, if necessary
		if ( !event.target ) {
			// Fixes #1925 where srcElement might not be defined either
			event.target 			= event.srcElement || document;
		}

		// check if target is a textnode (safari)
		if ( event.target.nodeType === 3 ) {
			event.target 			= event.target.parentNode;
		}

		// Add relatedTarget, if necessary
		if ( !event.relatedTarget && event.fromElement ) {
			event.relatedTarget 		= event.fromElement === event.target ? event.toElement : event.fromElement;
		}

		// Calculate pageX/Y if missing and clientX/Y available
		if ( event.pageX == null && event.clientX != null ) {
			var	event_document 	= event.target.ownerDocument || document,
				doc 			= event_document.documentElement,
				body 			= event_document.body;

			event.pageX 			= event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
			event.pageY 			= event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
		}

		//normalize pageX a little more, if we have a centered layout
		//get size exceeding the layout half it, and add it on the X
		//create new attribute normalized_x since pageX cant be amended if already set
		if(event.pageX){
			event.normalized_x = event.pageX - that.additional_layout_width;
		}

		// Add which for key events
		if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
			event.which 			= event.charCode != null ? event.charCode : event.keyCode;
		}

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if ( !event.metaKey && event.ctrlKey ) {
			event.metaKey 			= event.ctrlKey;
		}

		// Add which for click: 1 === left; 2 === middle; 3 === right
		// Note: button is not normalized, so don't use it
		if ( !event.which && event.button !== undefined ) {
			event.which 			= (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
		}

		return event;
	}



	/*
	 * Repair Events for issues out of IE
	 *
	 * @param handler 	The Handler for the Event
	 * @return 			A function to clean the event and then call the handler with it.
	 */
	var proxy_event = function(handler){
		return function(event){
			event = fix_event(event);
			handler(event);
		}
	}



	/*
	 * Cross-browser method to add an event listener to an element
	 *
	 * @param element 	Element to add the listener to
	 * @param type		Event to listen for, e.g. click
	 * @param handler	Method to execute when the event is emitted
	 * @return 			Void
	*/
	sauberfant.add_event = function (element, type, handler) {

		if (element.addEventListener) {
			//add fix, to heal event-element for non IE browsers and still be able to remove those Events
			if (!handler.$$guid) handler.$$guid = event_guid++;
			element[handler.$$guid] = proxy_event(handler);
			element.addEventListener(type, element[handler.$$guid], false);
		} else {
			// assign each event handler a unique ID
			if (!handler.$$guid) handler.$$guid = event_guid++;
			// create a hash table of event types for the element
			if (!element.events) element.events = {};
			// create a hash table of event handlers for each element/event pair
			var handlers = element.events[type];
			if (!handlers) {
				handlers = element.events[type] = {};
				// store the existing event handler (if there is one)
				if (element["on" + type]) {
					handlers[0] = element["on" + type];
				}
			}
			// store the event handler in the hash table
			handlers[handler.$$guid] = handler;
			// assign a global event handler to do all the work
			element["on" + type] = handle_event;
		}
	};



	/*
	 * Cross-browser method to remove an event listener from an element
	 * This method is the exact opposite of add_event
	 *
	 * @param element 	Element to remove the listener from
	 * @param type		The event the handler was bound to
	 * @param handler	The handler method that was bound to the event
	 * @return 			Void
	*/
	sauberfant.remove_event = function (element, type, handler) {
		if (element.removeEventListener) {
			element.removeEventListener(type, element[handler.$$guid], false);
		} else {
			// delete the event handler from the hash table
			if (element.events && element.events[type]) {
				delete element.events[type][handler.$$guid];
			}
		}
	};


})(sauberfant);
